#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QTextStream>


QStringList qslAmdAdapterList;

QList<__int8_t>  qilAmdAdapterIndex;


/* Memory allocation function */
void __stdcall *adl_main_memory_alloc(int isize)
{
    void *lpbuffer = malloc(isize);
    return lpbuffer;
}

void *getprocaddress(void *plibrary, const char * name)
{
    return dlsym(plibrary, name);
}

/*
  Returns the temperature (Celcius) of an AMD/Ati GPU.
*/
double get_temp(int devNumber)
{
    ADLTemperature temperature;

    temperature.iSize = sizeof(ADLTemperature);
    temperature.iTemperature = -273;
    if (ADL_OK != adl_overdrive5_temperature_get(devNumber, 0, &temperature))
        return UNKNOWN_DBL_VALUE;

    return temperature.iTemperature/1000;
}

double get_fanspeed(int devNumber, bool rpm)
{
    ADLFanSpeedValue fanspeedvalue;

    fanspeedvalue.iSize = sizeof(ADLFanSpeedValue);
    if (rpm)
    {
        fanspeedvalue.iSpeedType = ADL_DL_FANCTRL_SPEED_TYPE_RPM;
    }
    else
    {
        fanspeedvalue.iSpeedType = ADL_DL_FANCTRL_SPEED_TYPE_PERCENT;
    }
    fanspeedvalue.iFanSpeed = -1;
    if (ADL_OK != adl_overdrive5_fanspeed_get(devNumber, 0, &fanspeedvalue))
        return UNKNOWN_DBL_VALUE;

    return fanspeedvalue.iFanSpeed;
}

double set_fanspeed(int devNumber, unsigned int dw_fanSpeed)
{
    ADLFanSpeedValue fanspeedvalue;

    fanspeedvalue.iSize = sizeof(ADLFanSpeedValue);
    fanspeedvalue.iSpeedType = ADL_DL_FANCTRL_SPEED_TYPE_PERCENT;
    fanspeedvalue.iFanSpeed = dw_fanSpeed;
    if (ADL_OK != adl_overdrive5_fanspeed_set(devNumber, 0, &fanspeedvalue))
        return UNKNOWN_DBL_VALUE;

    return fanspeedvalue.iFanSpeed;
}

void set_default_fanspeed(int devNumber)
{
    adl_overdrive5_fanspeedToDefault_set(devNumber, 0);
}

int get_gpu_activity(int devNumber)
{
    ADLPMActivity lpAct;

    lpAct.iSize = sizeof(ADLPMActivity);

    if (ADL_OK != adl_overdrive5_currentActivity_get(devNumber, &lpAct))
        return -1;

    return lpAct.iActivityPercent;
}


/*
  Returns the number of AMD/Ati GPU sensors (temperature and fan
  speed).

  Return 0 if no AMD/Ati gpus or cannot get information.
*/
int init()
{
    LPAdapterInfo lpadapterinfo = NULL;
    int i, inumberadapters, inumberadaptersactive = 0;
    int lpstatus, iadapterindex;

    hdll = NULL;
    adl_main_control_done = 0;
    active_amd_adapters = NULL;
    hdll = dlopen("libatiadlxx.so", RTLD_LAZY|RTLD_GLOBAL);

    if (!hdll) {
        return -1;
    }

    adl_main_control_create = (ADL_MAIN_CONTROL_CREATE) getprocaddress(hdll, "ADL_Main_Control_Create");
    adl_main_control_destroy = (ADL_MAIN_CONTROL_DESTROY) getprocaddress(hdll, "ADL_Main_Control_Destroy");
    adl_adapter_numberofadapters_get = (ADL_ADAPTER_NUMBEROFADAPTERS_GET) getprocaddress(hdll, "ADL_Adapter_NumberOfAdapters_Get");
    adl_adapter_adapterinfo_get = (ADL_ADAPTER_ADAPTERINFO_GET) getprocaddress(hdll, "ADL_Adapter_AdapterInfo_Get");
    adl_adapter_active_get = (ADL_ADAPTER_ACTIVE_GET) getprocaddress(hdll, "ADL_Adapter_Active_Get");
    adl_overdrive5_temperature_get = (ADL_OVERDRIVE5_TEMPERATURE_GET) getprocaddress(hdll, "ADL_Overdrive5_Temperature_Get");
    adl_overdrive5_fanspeed_get = (ADL_OVERDRIVE5_FANSPEED_GET) getprocaddress(hdll, "ADL_Overdrive5_FanSpeed_Get");
    adl_overdrive5_fanspeed_set = (ADL_OVERDRIVE5_FANSPEED_SET) getprocaddress(hdll, "ADL_Overdrive5_FanSpeed_Set");
    adl_overdrive5_fanspeedToDefault_set = (ADL_OVERDRIVE5_FANSPEEDTODEFAULT_SET) getprocaddress(hdll, "ADL_Overdrive5_FanSpeedToDefault_Set");
    adl_overdrive5_currentActivity_get = (ADL_OVERDRIVE5_CURENTACTIVITY_GET) getprocaddress(hdll, "ADL_Overdrive5_CurrentActivity_Get");
    adl_overdrive5_thermaldevice_enum = (ADL_OVERDRIVE5_THERMALDEVICE_ENUM) getprocaddress(hdll, "ADL_Overdrive5_ThermalDevices_Enum");


    if (    !adl_main_control_create ||
            !adl_main_control_destroy ||
            !adl_adapter_numberofadapters_get ||
            !adl_adapter_adapterinfo_get ||
            !adl_overdrive5_temperature_get ||
            !adl_overdrive5_fanspeed_get ||
            !adl_overdrive5_fanspeed_set ||
            !adl_overdrive5_fanspeedToDefault_set||
            !adl_overdrive5_thermaldevice_enum
       )
    {
        return -1;
    }

    /* Initialize ADL. The second parameter is 1, which means:
       retrieve adapter information only for adapters that
       are physically present and enabled in the system */
    if (ADL_OK != adl_main_control_create(adl_main_memory_alloc, 1)) {
        //fprintf(stderr, _("ERROR: ADL Initialization Error!\n"));
        return 0;
    }
    adl_main_control_done = 1;

    /* Obtain the number of adapters for the system */
    if (ADL_OK != adl_adapter_numberofadapters_get(&inumberadapters)) {
        //fprintf(stderr, _("ERROR: Cannot get the number of adapters!\n"));
        return -1;
    }

    if (!inumberadapters)
        return -1;

    lpadapterinfo = (LPAdapterInfo) malloc(sizeof(AdapterInfo) * inumberadapters);
    memset(lpadapterinfo, '\0', sizeof(AdapterInfo) * inumberadapters);

    /* Get the AdapterInfo structure for all adapters in the system */
    adl_adapter_adapterinfo_get(lpadapterinfo,
                    sizeof(AdapterInfo) * inumberadapters);

    /* Repeat for all available adapters in the system */
    for (i = 0; i < inumberadapters; i++)
    {
        iadapterindex = lpadapterinfo[i].iAdapterIndex;

        if (ADL_OK != adl_adapter_active_get(iadapterindex, &lpstatus))
            continue;

        if (lpstatus != ADL_TRUE)
            /* count only if the adapter is active */
            continue;

        qslAmdAdapterList.push_back(QString(lpadapterinfo[i].strAdapterName));
        qilAmdAdapterIndex.push_back(lpadapterinfo[i].iAdapterIndex);

       if (!active_amd_adapters)
        {
            active_amd_adapters = (int *) malloc(sizeof(int));
            inumberadaptersactive = 1;
        }
        else
        {
            ++inumberadaptersactive;
            active_amd_adapters = (int *)realloc(active_amd_adapters, sizeof(int)*inumberadaptersactive);

        }
        active_amd_adapters[inumberadaptersactive-1] = iadapterindex;

    }

    free(lpadapterinfo);

    /* Each Adapter has one GPU temperature sensor and one fan
       control sensor */
    return 2*inumberadaptersactive;
}

void amd_cleanup()
{
    if (hdll) {
        if (adl_main_control_done)
            adl_main_control_destroy();
        dlclose(hdll);
    }

    if (active_amd_adapters) {
        free(active_amd_adapters);
        active_amd_adapters = NULL;
    }
}

Widget::Widget(QWidget *parent) :  QWidget(parent),  ui(new Ui::Widget)
{
    QTextStream out(stdout);

    ui->setupUi(this);

    iDeviceNumber = init();
    if (iDeviceNumber <= 0)
    {
        bAmdActive = false;
        QMessageBox::critical(this, tr("Critical error."), tr("AMD GPU not found."));
        this->~Widget();
    }
    else
    {
        bAmdActive = true;
    }
    ui->comboBox->insertItems(0,qslAmdAdapterList);

    out << "qslAmdAdapterList.size: " << qslAmdAdapterList.size()
        << " qilAmdAdapterIndex.size: " << qilAmdAdapterIndex.size()
        << " iDeviceNumber: " << iDeviceNumber
        << " active_amd_adapters[0]: " << active_amd_adapters[0]
        //<< " active_amd_adapters[0]: " << active_amd_adapters[0]
        << endl;

    this->startTimer(1000);
    iNeedFanSpeed = 0;
    iDevice = 0;
}

Widget::~Widget()
{
    for (int i=0; i< qilAmdAdapterIndex.size(); ++i)
    {
        set_default_fanspeed(qilAmdAdapterIndex[i]);
    }
    amd_cleanup();
    delete ui;
}


void Widget::on_pushButton_2_clicked()
{
    set_default_fanspeed(iDevice);
    setDialValue(int(get_fanspeed(iDevice,false)));
    set_default_fanspeed(iDevice);
}

void Widget::on_dial_valueChanged(int value)
{
    set_fanspeed(iDevice,(unsigned int) value);
    iNeedFanSpeed = value;
    setInt3(value);
}


void Widget::on_comboBox_currentIndexChanged(int index)
{
    iDevice = qilAmdAdapterIndex[index];
    setDialValue(int(get_fanspeed(iDevice,false)));
    dTemp = get_temp(iDevice);
    dFanSpeed = get_fanspeed(iDevice, true);
    gpuActivityPercent = get_gpu_activity(iDevice);
    setInt1((int)dTemp);
    setInt2((int)dFanSpeed);
    setInt4(gpuActivityPercent);
}
